/**
 ** 保留站的实现逻辑
 ** 标记一下，这里应该准备三个就绪队列，分别对应alu，乘法和除法
 ** 每次时钟上升沿，从队列中取出一个就绪块，送入到执行单元，运算得到结果
**/

module saveStation (
    input wire clk,
    input wire save_en_1,                  //保存到保留站的使能信号
    input wire save_en_2,
    input wire [2:0] alu_mode_1,           //运算模式--普通整数运算 or 整数乘除法 or 浮点
    input wire [2:0] alu_mode_2,
    input wire [3:0] alu_ctrl_1,           //运算器的控制信号
    input wire [3:0] alu_ctrl_2,
    input wire [3:0] complex_ctrl_1,       //乘除法的控制信号
    input wire [3:0] complex_ctrl_2,       
    input wire rs_1_ready,             //寄存器是否准备就绪的标记
    input wire rt_1_ready,             
    input wire rs_2_ready,             
    input wire rt_2_ready,             
    input wire [4:0] rs_1_relation,    //和rs寄存器关联的ROB号
    input wire [4:0] rt_1_relation,    //和rt寄存器关联的ROB号
    input wire [4:0] rd_1_relation,    //和rd寄存器关联的ROB号
    input wire [4:0] rs_2_relation,    //和rs寄存器关联的ROB号
    input wire [4:0] rt_2_relation,    //和rt寄存器关联的ROB号
    input wire [4:0] rd_2_relation,    //和rd寄存器关联的ROB号
    input wire [31:0] rs_1_value,      //寄存器的值
    input wire [31:0] rt_1_value,      
    input wire [31:0] rs_2_value,      //寄存器的值
    input wire [31:0] rt_2_value,    
    input wire alu_commit_en, mul_commit_en, div_commit_en,
    input wire [4:0] alu_commit_save_no, mul_commit_save_no, div_commit_save_no,
    input wire [4:0] alu_commit_rob_no, mul_commit_rob_no, div_commit_rob_no, 
    input wire [31:0] alu_commit_value, mul_commit_value, div_commit_value, 
    output reg [3:0] common_ctrl,      
    output reg [31:0] alu_X, alu_Y,
    output reg alu_ready_en, div_ready_en, mul_ready_en,
    output reg [4:0] alu_rd_rob_no, mul_rd_rob_no, div_rd_rob_no, 
    output reg [4:0] alu_save_no, mul_save_no, div_save_no,
    output reg [3:0] mul_ctrl,
    output reg [31:0] mul_X, mul_Y,
    output reg [3:0] div_ctrl,
    output reg [31:0] div_X, div_Y
);
    //保存保留站信息的数组
    reg [31:0] rs_ok, rt_ok;                        //寄存器是否准备好的标记位
    reg [31:0] rs_value[0:31], rt_value[0:31];      //寄存器对应的值
    reg [4:0] rs_relation[0:31];                    //当前源寄存器依赖的ROB号
    reg [4:0] rt_relation[0:31];
    reg [4:0] rd_relation[0:31];                    //当前目标寄存器对应的ROB  
    reg [3:0] alu_ctrl[0:31];                       //运算器的控制信号
    reg [2:0] alu_mode[0:31];                       //运算模式，用来选择运算器


    //定义空闲队列，方便查找到空闲可用的保留站条目
    reg [4:0] freeQueue[0:31];
    reg [4:0] free_tail;
    reg [4:0] free_head;
    reg [4:0] free_temp_index;

    //定义就绪队列，方便查找到已经就绪的条目，可直接供给运算单元
    reg [4:0] okQueue[0:31];
    reg [4:0] ok_tail;
    reg [4:0] ok_head;
    reg [4:0] ok_temp_index;

    //普通运算器就绪队列及相关指针
    reg [4:0] alu_ok_queue [0:15];
    reg [4:0] alu_ok_tail;
    reg [4:0] alu_ok_head;
    reg [4:0] alu_ok_temp_index;

    //整数乘法器就绪队列及相关指针
    reg [4:0] mul_ok_queue [0:15];
    reg [4:0] mul_ok_tail;
    reg [4:0] mul_ok_head;
    reg [4:0] mul_ok_temp_index;

    //整数除法器就绪队列及相关指针
    reg [4:0] div_ok_queue [0:15];
    reg [4:0] div_ok_tail;
    reg [4:0] div_ok_head;
    reg [4:0] div_ok_temp_index;

    //定义Hash表，方便查出与ROB保留站关联的条目
    reg [4:0] map[0:31][0:7];          //32个ROB，每一个ROB对应最多八个关联
    reg [5:0] mapLen[0:31];            //映射表的长度，mapLen[i]表示i号ROB关联保留站的个数
    //临时变量
    reg [5:0] temp_len;
    reg [4:0] temp_no;
    reg [4:0] alu_temp_map_no, mul_temp_map_no, div_temp_map_no;
    reg reg_temp_ready_1, reg_temp_ready_2, reg_temp_ready_3;

    //遍历需要的指针
    integer i, j, k;
    

    initial begin
        free_temp_index = 0;
        //初始化空闲队列指针
        free_head = 0;
        free_tail = 31;  //初始情况下，所有的保留站条目都是可用的，即队列是满的
        //初始化就绪队列指针
        ok_head = 0;
        ok_tail = 0;
        alu_ok_tail = 0;
        alu_ok_head = 0;
        mul_ok_tail = 0;
        mul_ok_head = 0;
        div_ok_tail = 0;
        div_ok_head = 0;
        //初始化保留站和队列
        for (i=0; i<32; i++) begin
            rs_ok[i] = 0; 
            rs_ok[i] = 0;
            freeQueue[i] = i;
            mapLen[i] = 0;
        end
    end

    //记录写入保留站
    always @(rd_1_relation or rd_2_relation) begin
        if (save_en_1==1) begin
            //处理第一条指令
            //从空闲队列取出一个条目供当前记录使用
            free_temp_index = freeQueue[free_head];
            //将数据写入到保留站备用
            rs_value[free_temp_index] = rs_1_value;
            rt_value[free_temp_index] = rt_1_value;
            rd_relation[free_temp_index] = rd_1_relation;
            alu_mode[free_temp_index] = alu_mode_1;
            if (alu_mode_1 == 3'b000) begin   //普通alu运算
                alu_ctrl[free_temp_index] = alu_ctrl_1;
            end
            else if (alu_mode_1 == 3'b001 || alu_mode_1 == 3'b010) begin  //乘除法
                alu_ctrl[free_temp_index] = complex_ctrl_1;  
            end
            else begin
                alu_ctrl[free_temp_index] = alu_ctrl_1;
            end
           

            if (rs_1_ready==1) begin 
                //操作数rs，已经准备好
                rs_ok[free_temp_index] = 1;   
            end
            else begin
                rs_ok[free_temp_index] = 0;
                rs_relation[free_temp_index] = rs_1_relation;
                //维护ROB关联映射表
                map[rs_1_relation][mapLen[rs_1_relation]] = free_temp_index;
                mapLen[rs_1_relation] = (mapLen[rs_1_relation]+1)%16;
            end

            if (rt_1_ready==1) begin
                //操作数rt，已经准备好
                rt_ok[free_temp_index] = 1;
            end
            else begin
                rt_ok[free_temp_index] = 0;
                rt_relation[free_temp_index] = rt_1_relation;
                //维护ROB关联映射表
                map[rt_1_relation][mapLen[rt_1_relation]] = free_temp_index;
                mapLen[rt_1_relation] = (mapLen[rt_1_relation]+1)%16;
            end            
            
            //如果两个操作都准备好了，就放入就绪队列
            if (rs_1_ready==1 && rt_1_ready==1) begin
                //根据所运行模式，加入到不同的队列中
                if (alu_mode_1 == 3'b000) begin  //普通alu运算
                    alu_ok_queue[alu_ok_tail] = free_temp_index;
                    alu_ok_tail = (alu_ok_tail+1)%16;
                end
                else if (alu_mode_1 == 3'b001) begin  //整数乘法
                    mul_ok_queue[mul_ok_tail] = free_temp_index;  
                    mul_ok_tail = (mul_ok_tail+1)%16;
                end
                else if (alu_mode_1 == 3'b010) begin  //整数除法
                    div_ok_queue[div_ok_tail] = free_temp_index;
                    div_ok_tail = (div_ok_tail+1)%16;
                end
                else begin
                    //TODO 后面可能会支持浮点运算之类的
                end
            end

            //指针后移，使用下一块空闲条目
            free_head=(free_head+1)%32;
        end

        if (save_en_2==1) begin
            //处理第二条指令
            //从空闲队列取出一个条目供当前记录使用
            free_temp_index = freeQueue[free_head];
            //将数据写入到保留站备用
            rs_value[free_temp_index] = rs_2_value;
            rt_value[free_temp_index] = rt_2_value;
            rd_relation[free_temp_index] = rd_2_relation;
            alu_mode[free_temp_index] = alu_mode_2;
            if (alu_mode_2 == 3'b000) begin   //普通alu运算
                alu_ctrl[free_temp_index] = alu_ctrl_2;
            end
            else if (alu_mode_2 == 3'b001 || alu_mode_2 == 3'b010) begin  //乘除法
                alu_ctrl[free_temp_index] = complex_ctrl_2;  
            end
            else begin
                alu_ctrl[free_temp_index] = alu_ctrl_2;
            end

            if (rs_2_ready==1) begin
                rs_ok[free_temp_index] = 1;
            end
            else begin
                rs_ok[free_temp_index] = 0;
                rs_relation[free_temp_index] = rs_2_relation;
                //维护ROB关联映射表
                map[rs_2_relation][mapLen[rs_2_relation]] = free_temp_index;
                mapLen[rs_2_relation] = (mapLen[rs_2_relation]+1)%16;
                //$display(rs_2_relation, " -- ", mapLen[rs_2_relation]);
            end

            if (rt_2_ready==1) begin
                rt_ok[free_temp_index] = 1;
            end
            else begin
                rt_ok[free_temp_index] = 0;
                rt_relation[free_temp_index] = rt_2_relation;
                //维护ROB关联映射表
                map[rt_2_relation][mapLen[rt_2_relation]] = free_temp_index;
                mapLen[rt_2_relation] = (mapLen[rt_2_relation]+1)%16;
            end          


            
            //如果两个操作都准备好了，就放入就绪队列
            if (rs_2_ready==1 && rt_2_ready==1) begin
                //$display("保留站号: ", free_temp_index);
                //根据所运行模式，加入到不同的队列中
                if (alu_mode_2 == 3'b000) begin  //普通alu运算
                    alu_ok_queue[alu_ok_tail] = free_temp_index;
                    alu_ok_tail = (alu_ok_tail+1)%16;
                end
                else if (alu_mode_2 == 3'b001) begin  //整数乘法
                    mul_ok_queue[mul_ok_tail] = free_temp_index;  
                    mul_ok_tail = (mul_ok_tail+1)%16;
                end
                else if (alu_mode_2 == 3'b010) begin  //整数除法
                    div_ok_queue[div_ok_tail] = free_temp_index;
                    div_ok_tail = (div_ok_tail+1)%16;
                end
                else begin
                    //TODO 后面可能会支持浮点运算之类的
                end
            end

            //指针后移，使用下一块空闲条目
            free_head=(free_head+1)%32;
        end
    end

    //发射逻辑，每个时钟上升沿输出一个就绪指令
    always @(posedge clk) begin
        //乘法操作
        if (mul_ok_head != mul_ok_tail) begin
            mul_ok_temp_index = mul_ok_queue[mul_ok_head];
            mul_ok_head = (mul_ok_head+1)%32;
            mul_ctrl = alu_ctrl[mul_ok_temp_index];
            mul_X = rs_value[mul_ok_temp_index];
            mul_Y = rt_value[mul_ok_temp_index];
            mul_save_no = mul_ok_temp_index;
            mul_rd_rob_no = rd_relation[mul_ok_temp_index];
            mul_ready_en = 1;
        end
        else begin
            mul_ready_en = 0;
        end
    end

    always @(posedge clk) begin
        //除法操作
        if (div_ok_head != div_ok_tail) begin
            div_ok_temp_index = div_ok_queue[div_ok_head];
            div_ok_head = (div_ok_head+1)%32;
            div_ctrl = alu_ctrl[div_ok_temp_index];
            div_X = rs_value[div_ok_temp_index];
            div_Y = rt_value[div_ok_temp_index];
            div_save_no = div_ok_temp_index;
            div_rd_rob_no = rd_relation[div_ok_temp_index];
            div_ready_en = 1;
        end
        else begin
            div_ready_en = 0;
        end
    end

    always @(posedge clk) begin
        //普通ALU操作
        //$display("就绪队列头指针: ", alu_ok_head, " -尾指针: ", alu_ok_tail);
        if (alu_ok_head != alu_ok_tail) begin  //判断队列不为空时
            //$display("保留站: ", alu_ok_temp_index, " x: ", alu_X, " y: ", alu_Y);
            alu_ok_temp_index = alu_ok_queue[alu_ok_head];
            alu_ok_head = (alu_ok_head+1)%16;
            common_ctrl = alu_ctrl[alu_ok_temp_index];
            alu_X = rs_value[alu_ok_temp_index];
            alu_Y = rt_value[alu_ok_temp_index];
            alu_save_no = alu_ok_temp_index;
            alu_rd_rob_no = rd_relation[alu_ok_temp_index];
            alu_ready_en = 1;  //使能位，告知运算单元可以开始工作了
            //$display("头指针: ", alu_ok_head, " -尾指针: ", alu_ok_tail, " 输出数据: ", alu_rd_rob_no, " - ", alu_X, " - ", alu_Y);
        end
        else begin
            alu_ready_en = 0; 
        end
    end

   //内容更新
   always @(alu_commit_en or alu_commit_save_no or alu_commit_rob_no or alu_commit_value) begin
        //普通运算写回
        if (alu_commit_en==1) begin
            for (i=0; i<16; i++) begin
                alu_temp_map_no = map[alu_commit_rob_no][i];
                //更新保留站的值
                if (i < mapLen[alu_commit_rob_no]) begin
                    //对比rs和rt关联的ROB，如果对应上了，则更新寄存器状态和值
                    if (rs_relation[alu_temp_map_no]==alu_commit_rob_no) begin
                        rs_value[alu_temp_map_no] = alu_commit_value;
                        rs_ok[alu_temp_map_no] = 1;
                        //$display("rs-修改保留站: ", alu_temp_map_no, " v1: ", rs_value[alu_temp_map_no], " v2: ", alu_commit_value);
                    end
                    else if (rt_relation[alu_temp_map_no]==alu_commit_rob_no) begin
                        rt_value[alu_temp_map_no] = alu_commit_value;
                        rt_ok[alu_temp_map_no] = 1;
                        //$display("rt-修改保留站: ", alu_temp_map_no, " v1: ", rt_value[alu_temp_map_no], " v2: ", alu_commit_value);
                    end
                    else begin
                        //对比不上, 不做处理
                    end

                    if (rt_ok[alu_temp_map_no] && rs_ok[alu_temp_map_no]) begin
                        if (alu_mode[alu_temp_map_no] == 3'b000) begin
                            alu_ok_queue[alu_ok_tail] = alu_temp_map_no;
                            alu_ok_tail = (alu_ok_tail+1)%16;
                        end
                        else if (alu_mode[alu_temp_map_no] == 3'b001) begin
                            mul_ok_queue[mul_ok_tail] = alu_temp_map_no;  
                            mul_ok_tail = (mul_ok_tail+1)%16;
                        end
                        else if (alu_mode[alu_temp_map_no] == 3'b010) begin
                            div_ok_queue[div_ok_tail] = alu_temp_map_no;
                            div_ok_tail = (div_ok_tail+1)%16;
                        end
                        else begin

                        end
                    end
                end
            end
            // //将当前写回的指令所占据的保留站条目释放，此条目加入空闲块队列
            freeQueue[free_tail] = alu_commit_save_no;
            free_tail=(free_tail+1)%32;
            //将长度改成0，冲刷当前映射表条目
            mapLen[alu_commit_rob_no] = 0;
        end
   end

   always @(mul_commit_en or mul_commit_save_no or mul_commit_rob_no or mul_commit_value) begin
        //乘法运算写回
        //$display("mul_commit_rob_no: ", mul_commit_rob_no, "mul_commit_value: ", mul_commit_value);
        if (mul_commit_en==1) begin
            for (j=0; j<16; j++) begin
                mul_temp_map_no = map[mul_commit_rob_no][j];
                //更新保留站的值
                if (j < mapLen[mul_commit_rob_no]) begin
                    if (rs_relation[mul_temp_map_no]==mul_commit_rob_no) begin
                        rs_value[mul_temp_map_no] = mul_commit_value;
                        rs_ok[mul_temp_map_no] = 1;
                        //$display("rs-修改保留站: ", mul_temp_map_no, " v1: ", rs_value[mul_temp_map_no], " v2: ", mul_commit_value);
                    end
                    else if (rt_relation[mul_temp_map_no]==mul_commit_rob_no) begin
                        rt_value[mul_temp_map_no] = mul_commit_value;
                        rt_ok[mul_temp_map_no] = 1;
                        //$display("rt-修改保留站: ", mul_temp_map_no, " v1: ", rt_value[mul_temp_map_no], " v2: ", mul_commit_value);
                    end
                    else begin
                        //对比不上, 不做处理
                    end

                    // if (mul_temp_map_no==17) begin
                    //     $display("mode: ", alu_mode[mul_temp_map_no], " 指针: ", alu_ok_tail, " - ", (alu_ok_tail+1)%32);
                    //     //$display("rs准备状态: ", rs_ok[mul_temp_map_no], " rs的值: ", rs_value[mul_temp_map_no], " rt准备状态: ", rt_ok[mul_temp_map_no], " rt的值: ", rt_value[mul_temp_map_no]);
                    // end
                    //$display("rob_no: ", mul_commit_rob_no, " 更新保留站: ", mul_temp_map_no, " 状态: ", rt_ok[mul_temp_map_no], " -- ", rs_ok[mul_temp_map_no]);
                    //更新完毕，将两个操作数都达到就绪状态的内容加入就绪队列
                    if (rt_ok[mul_temp_map_no]==1 && rs_ok[mul_temp_map_no]==1) begin
                        //根据所运行模式，加入到不同的队列中
                        if (alu_mode[mul_temp_map_no] == 3'b000) begin  //普通alu运算
                            alu_ok_queue[alu_ok_tail] = mul_temp_map_no;
                            alu_ok_tail = (alu_ok_tail+1)%16;
                        end
                        else if (alu_mode[mul_temp_map_no] == 3'b001) begin  //整数乘法
                            mul_ok_queue[mul_ok_tail] = mul_temp_map_no;  
                            mul_ok_tail = (mul_ok_tail+1)%16;
                        end
                        else if (alu_mode[mul_temp_map_no] == 3'b010) begin  //整数除法
                            div_ok_queue[div_ok_tail] = mul_temp_map_no;
                            div_ok_tail = (div_ok_tail+1)%16;
                        end
                        else begin
                            //TODO 后面可能会支持浮点运算之类的
                        end
                    end
                end
            end
            // //将当前写回的指令所占据的保留站条目释放，此条目加入空闲块队列
            freeQueue[free_tail] = mul_commit_save_no;
            free_tail=(free_tail+1)%32;
            //将长度改成0，冲刷当前映射表条目
            mapLen[mul_commit_rob_no] = 0;
        end
   end

   always @(div_commit_en or div_commit_save_no or div_commit_rob_no or div_commit_value) begin
        //除法运算写回
        if (div_commit_en==1) begin
            for (j=0; j<16; j++) begin
                div_temp_map_no = map[div_commit_rob_no][j];
                //更新保留站的值
                if (j < mapLen[div_commit_rob_no]) begin
                    if (rs_relation[div_temp_map_no]==div_commit_rob_no) begin
                        rs_value[div_temp_map_no] = div_commit_value;
                        rs_ok[div_temp_map_no] = 1;
                        //$display("改了rs: ", div_temp_map_no);
                    end
                    else if (rt_relation[div_temp_map_no]==div_commit_rob_no) begin
                        rt_value[div_temp_map_no] = div_commit_value;
                        rt_ok[div_temp_map_no] = 1;
                        //$display("改了rt: ", div_temp_map_no);
                    end
                    else begin
                        //对比不上, 不做处理
                    end
                    //$display("初始状态: ", rt_ok[div_temp_map_no], " -- ", rs_ok[div_temp_map_no], " 模式: ", alu_mode[div_temp_map_no]);
                    //更新完毕，将两个操作数都达到就绪状态的内容加入就绪队列
                    if (rt_ok[div_temp_map_no]==1 && rs_ok[div_temp_map_no]==1) begin
                        //根据所运行模式，加入到不同的队列中
                        if (alu_mode[div_temp_map_no] == 3'b000) begin  //普通alu运算
                            alu_ok_queue[alu_ok_tail] = div_temp_map_no;
                            alu_ok_tail = (alu_ok_tail+1)%16;
                        end
                        else if (alu_mode[div_temp_map_no] == 3'b001) begin  //整数乘法
                            mul_ok_queue[mul_ok_tail] = div_temp_map_no;  
                            mul_ok_tail = (mul_ok_tail+1)%16;
                        end
                        else if (alu_mode[div_temp_map_no] == 3'b010) begin  //整数除法
                            div_ok_queue[div_ok_tail] = div_temp_map_no;
                            div_ok_tail = (div_ok_tail+1)%16;
                        end
                        else begin
                            //TODO 后面可能会支持浮点运算之类的
                        end
                    end
                end
            end
            // //将当前写回的指令所占据的保留站条目释放，此条目加入空闲块队列
            freeQueue[free_tail] = div_commit_save_no;
            free_tail=(free_tail+1)%32;
            //将长度改成0，冲刷当前映射表条目
            mapLen[div_commit_rob_no] = 0;
        end
   end
endmodule